home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue47 / Differ / NewParse.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-04-26  |  16.6 KB  |  633 lines

  1. { *****************************************************
  2.                  NewParse Unit
  3.  
  4.                   Paul Warren
  5.          HomeGrown Software Development
  6.        (c) 1997 Langley British Columbia.
  7.                 (604) 856-6523
  8.          e-mail:  hg_soft@uniserve.com
  9.     Home page: http://users.uniserve.com/~hg_soft
  10.  
  11.     04/26/99 - Added FULLLINECOUNT define so that you can
  12.     select whether to count multiline comments. Default is
  13.     off so that the PasToWeb code will work properly.
  14.  
  15.   ***************************************************** }
  16.  
  17. unit Newparse;
  18. { $DEFINE DEBUG}
  19. { $DEFINE FULLLINECOUNT}
  20.  
  21. interface
  22.  
  23. uses Classes, Consts, SysUtils, Dialogs;
  24.  
  25. type
  26.   TParserClass = class of TCustomParser;
  27.  
  28.   TCustomParser = class
  29.   private
  30.     { private declarations }
  31.     FStream: TStream;
  32.     FOrigin: Longint;
  33.     FBuffer: PChar;
  34.     FBufPtr: PChar;
  35.     FBufEnd: PChar;
  36.     FSourcePtr: PChar;
  37.     FSourceEnd: PChar;
  38.     FTokenPtr: PChar;
  39.     FStringPtr: PChar;
  40.     FSourceLine: Integer;
  41.     FSaveChar: Char;
  42.     FToken: Char;
  43.     procedure ReadBuffer;
  44.     procedure SkipBlanks;
  45.     {$IFDEF Win32}
  46.     procedure Error(const Ident: string); virtual;
  47.     {$ELSE}
  48.     procedure Error(MessageID: Word); virtual;
  49.     {$ENDIF}
  50.     procedure ErrorStr(const Message: string);
  51.   public
  52.     { public declarations }
  53.     constructor Create(Stream: TStream); virtual;
  54.     destructor Destroy; override;
  55.     function NextToken: Char; virtual;
  56.     function TokenString: string; virtual;
  57.     function SourcePos: Longint;
  58.     property Token: Char read FToken;
  59.     property SourceLine: integer read FSourceLine;
  60.   end;
  61.  
  62.   TCSVParser = class(TCustomParser)
  63.   private
  64.     { private declarations }
  65.   public
  66.     { public declarations }
  67.     function TokenString: string; override;
  68.     function NextToken: char; override;
  69.   end;
  70.  
  71.   TTextParser = class(TCustomParser)
  72.   private
  73.     { private declarations }
  74.   public
  75.     { public declarations }
  76.     function NextToken: Char; override;
  77.   end;
  78.  
  79.   TPasParser = class(TTextParser)
  80.   private
  81.     { private declarations }
  82.   public
  83.     { public declarations }
  84.     function NextToken: Char; override;
  85.   end;
  86.  
  87. const
  88.   toComment = Char(5);
  89.  
  90. type
  91.   TEnhPasParser = class(TPasParser)
  92.   private
  93.     { private declarations }
  94.   public
  95.     { public declarations }
  96.     function TokenString: string; override;
  97.     function NextToken: Char; override;
  98.   end;
  99.  
  100. const
  101.   toOpenTag = Char(6);
  102.   toCloseTag = Char(7);
  103.  
  104. type
  105.   THtmlParser = class(TTextParser)
  106.   private
  107.     { private declarations }
  108.   public
  109.     { public declarations }
  110.     function TokenString: string; override;
  111.     function NextToken: Char; override;
  112.   end;
  113.  
  114. var
  115.   Log: TextFile;
  116.  
  117. implementation
  118.  
  119. const
  120.   ParseBufSize: integer = 4096;
  121.  
  122. { TCustomParser }
  123. constructor TCustomParser.Create(Stream: TStream);
  124. begin
  125.   FStream := Stream;
  126.   GetMem(FBuffer, ParseBufSize);
  127.   FBuffer[0] := #0;
  128.   FBufPtr := FBuffer;
  129.   FBufEnd := FBuffer + ParseBufSize;
  130.   FSourcePtr := FBuffer;
  131.   FSourceEnd := FBuffer;
  132.   FTokenPtr := FBuffer;
  133.   FSourceLine := 1;
  134.   {$IFDEF DEBUG}
  135.   writeln(log,'');
  136.   writeln(log, 'FBuffer FBufPtr FSrcPtr   FSrcEnd FBufEnd Pos Occured');
  137.   writeln(log,'');
  138.   writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position,' on create');
  139.   {$ENDIF}
  140.   NextToken;
  141. end;
  142.  
  143. destructor TCustomParser.Destroy;
  144. begin
  145.   if FBuffer <> nil then
  146.   begin
  147.     FStream.Seek(Longint(FTokenPtr) - Longint(FSourceEnd), 1);
  148.     FreeMem(FBuffer, ParseBufSize);
  149.   end;
  150. end;
  151.  
  152. procedure TCustomParser.ReadBuffer;
  153. var
  154.   Count: Integer;
  155. begin
  156.   try
  157.     Inc(FOrigin, FSourcePtr - FBuffer);
  158.     FSourceEnd[0] := FSaveChar;
  159.   {$IFDEF DEBUG}
  160.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^,' ', LongInt(FSourceEnd),' ',LongInt(FBufEnd), ' ',FStream.Position, ' before read');
  161.   {$ENDIF}
  162.     Count := FBufPtr - FSourcePtr;
  163.     if Count <> 0 then Move(FSourcePtr[0], FBuffer[0], Count);
  164.     FBufPtr := FBuffer + Count;
  165.     Inc(FBufPtr, FStream.Read(FBufPtr[0], FBufEnd - FBufPtr));
  166.   {$IFDEF DEBUG}
  167.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position, ' after read');
  168.   {$ENDIF}
  169.     FSourcePtr := FBuffer;
  170.     FSourceEnd := FBufPtr;
  171.     if FSourceEnd = FBufEnd then
  172.     begin
  173.       FSourceEnd := LineStart(FBuffer, FSourceEnd - 1);
  174.       if FSourceEnd = FBuffer then Error(SLineTooLong);
  175.     end;
  176.     FSaveChar := FSourceEnd[0];
  177.     FSourceEnd[0] := #0;
  178.   except
  179.     on EStreamError do
  180.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  181.         [mbOK],0);
  182.     on EAccessViolation do
  183.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  184.         [mbOK],0);
  185.   end;
  186. end;
  187.  
  188. function TCustomParser.NextToken: Char;
  189. begin
  190.   FToken := FSourcePtr^;
  191.   if FToken <> toEOF then Inc(FSourcePtr);
  192.   Result := FToken;
  193. end;
  194.  
  195. procedure TCustomParser.SkipBlanks;
  196. begin
  197.   while True do
  198.   begin
  199.     case FSourcePtr^ of
  200.       #0:
  201.         begin
  202.           ReadBuffer;
  203.           if FSourcePtr^ = #0 then Exit;
  204.           Continue;
  205.         end;
  206.       #10:
  207.         Inc(FSourceLine);
  208.       #33..#255:
  209.         Exit;
  210.     end;
  211.     Inc(FSourcePtr);
  212.   end;
  213. end;
  214.  
  215. function TCustomParser.TokenString: string;
  216. var
  217.   L: Integer;
  218. begin
  219.   if (FToken = toString) then
  220.     L := FStringPtr - FTokenPtr else
  221.     L := FSourcePtr - FTokenPtr;
  222.   {$IFDEF Win32}
  223.   SetString(Result, FTokenPtr, L);
  224.   {$ELSE}
  225.   if L > 255 then L := 255;
  226.   Result[0] := Char(L);
  227.   {$ENDIF}
  228.   Move(FTokenPtr[0], Result[1], L);
  229. end;
  230.  
  231. {$IFDEF Win32}
  232. procedure TCustomParser.Error(const Ident: string);
  233. begin
  234.   ErrorStr(Ident);
  235. end;
  236.  
  237. procedure TCustomParser.ErrorStr(const Message: string);
  238. begin
  239.   raise EParserError.CreateFmt(SParseError, [Message, FSourceLine]);
  240. end;
  241. {$ELSE}
  242. procedure TCustomParser.Error(MessageID: Word);
  243. begin
  244.   ErrorStr(LoadStr(MessageID));
  245. end;
  246.  
  247. procedure TCustomParser.ErrorStr(const Message: string);
  248. begin
  249.   raise EParserError.Create(FmtLoadStr(SParseError, [Message, FSourceLine]));
  250. end;
  251. {$ENDIF}
  252.  
  253. function TCustomParser.SourcePos: Longint;
  254. begin
  255.   Result := FOrigin + (FTokenPtr - FBuffer);
  256. end;
  257.  
  258. { TCSVParser }
  259. function TCSVParser.TokenString: string;
  260. var
  261.   L: Integer;
  262. begin
  263.   if (FToken = toSymbol) then
  264.     L := FStringPtr - FTokenPtr else
  265.     L := FSourcePtr - FTokenPtr;
  266.   {$IFDEF Win32}
  267.   SetString(Result, FTokenPtr, L);
  268.   {$ELSE}
  269.   if L > 255 then L := 255;
  270.   Result[0] := Char(L);
  271.   {$ENDIF}
  272.   Move(FTokenPtr[0], Result[1], L);
  273. end;
  274.  
  275. function TCSVParser.NextToken: Char;
  276. begin
  277.   SkipBlanks;
  278.   FTokenPtr := FSourcePtr;
  279.   case FSourcePtr^ of
  280.     'A'..'Z', 'a'..'z', '_':
  281.       begin
  282.         Inc(FSourcePtr);
  283.         FStringPtr := FSourcePtr;
  284.         while true do
  285.         begin
  286.           case FSourcePtr^ of
  287.             ',': Break;
  288.             #0: Break;
  289.           end;
  290.           FStringPtr^ := FSourcePtr^;
  291.           Inc(FStringPtr);
  292.           Inc(FSourcePtr);
  293.         end;
  294.         FToken := toSymbol;
  295.         Result := FToken;
  296.       end;
  297.     '-', '0'..'9':
  298.       begin
  299.         Inc(FSourcePtr);
  300.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  301.         FToken := toInteger;
  302.         Result := FToken;
  303.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  304.         begin
  305.           Inc(FSourcePtr);
  306.           FToken := toFloat;
  307.           Result := FToken;
  308.         end;
  309.       end;
  310.     else Result := inherited NextToken;
  311.   end;
  312. end;
  313.  
  314. { TTextParser }
  315. function TTextParser.NextToken: Char;
  316. begin
  317.   SkipBlanks;
  318.   FTokenPtr := FSourcePtr;
  319.   case FSourcePtr^ of
  320.     'A'..'Z', 'a'..'z', '_':
  321.       begin
  322.         Inc(FSourcePtr);
  323.         while True do
  324.           case FSourcePtr^ of
  325.             'A'..'Z', 'a'..'z', '0'..'9', '_': Inc(FSourcePtr);
  326.             '''': begin  { apostrophies }
  327.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  328.                 else Break;
  329.               end;
  330.             '-': begin  { hyphenated words }
  331.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  332.                 else Break;
  333.               end;
  334.             else Break;
  335.           end;
  336.         FToken := toSymbol;
  337.         Result := FToken;
  338.       end;
  339.     '-', '0'..'9':
  340.       begin
  341.         Inc(FSourcePtr);
  342.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  343.         FToken := toInteger;
  344.         Result := FToken;
  345.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  346.         begin
  347.           Inc(FSourcePtr);
  348.           FToken := toFloat;
  349.           Result := FToken;
  350.         end;
  351.       end;
  352.     else Result := inherited NextToken;
  353.   end;
  354. end;
  355.  
  356. { TPasParser }
  357. function TPasParser.NextToken: Char;
  358. var
  359.   I: integer;
  360. begin
  361.   SkipBlanks;
  362.   FTokenPtr := FSourcePtr;
  363.   case FSourcePtr^ of
  364.     'A'..'Z', 'a'..'z', '_':
  365.       begin
  366.         Inc(FSourcePtr);
  367.         while FSourcePtr^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(FSourcePtr);
  368.         FToken := toSymbol;
  369.         Result := FToken;
  370.       end;
  371.     '#', '''':
  372.       begin
  373.         FStringPtr := FSourcePtr;
  374.         while True do
  375.           case FSourcePtr^ of
  376.             '#':
  377.               begin
  378.                 Inc(FSourcePtr);
  379.                 I := 0;
  380.                 while FSourcePtr^ in ['0'..'9'] do
  381.                 begin
  382.                   I := I * 10 + (Ord(FSourcePtr^) - Ord('0'));
  383.                   Inc(FSourcePtr);
  384.                 end;
  385.                 FStringPtr^ := Chr(I);
  386.                 Inc(FStringPtr);
  387.               end;
  388.             '''':
  389.               begin
  390.                 Inc(FSourcePtr);
  391.                 while True do
  392.                 begin
  393.                   case FSourcePtr^ of
  394.                     #0, #10, #13:
  395.                       Error(SInvalidString);
  396.                     '''':
  397.                       begin
  398.                         Inc(FSourcePtr);
  399.                         if FSourcePtr^ <> '''' then Break;
  400.                       end;
  401.                   end;
  402.                   FStringPtr^ := FSourcePtr^;
  403.                   Inc(FStringPtr);
  404.                   Inc(FSourcePtr);
  405.                 end;
  406.               end;
  407.           else
  408.             Break;
  409.           end;
  410.         FToken := toString;
  411.         Result := FToken;
  412.       end;
  413.     '$':
  414.       begin
  415.         FToken := FSourcePtr^;  { assume NOT an integer }
  416.         Result := FToken;
  417.         Inc(FSourcePtr);
  418.         while true do
  419.         begin
  420.           case FSourcePtr^ of
  421.             '0'..'9', 'A'..'F', 'a'..'f': Inc(FSourcePtr);
  422.             else Break;
  423.           end;
  424.           FToken := toInteger;
  425.           Result := FToken;
  426.         end;
  427.       end;
  428.   (*  '-', '0'..'9':
  429.       begin
  430.         Inc(FSourcePtr);
  431.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  432.         FToken := toInteger;
  433.         Result := FToken;
  434.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  435.         begin
  436.           Inc(FSourcePtr);
  437.           FToken := toFloat;
  438.           Result := FToken;
  439.         end;
  440.       end;  *)
  441.     else Result := inherited NextToken;
  442.   end;
  443. end;
  444.  
  445. { TEnhPasParser }
  446. function TEnhPasParser.TokenString: string;
  447. var
  448.   L: Integer;
  449. begin
  450.   if (FToken = toString) or (FToken = toComment) then
  451.     L := FStringPtr - FTokenPtr else
  452.     L := FSourcePtr - FTokenPtr;
  453.   {$IFDEF Win32}
  454.   SetString(Result, FTokenPtr, L);
  455.   {$ELSE}
  456.   if L > 255 then L := 255;
  457.   Result[0] := Char(L);
  458.   {$ENDIF}
  459.   Move(FTokenPtr[0], Result[1], L);
  460. end;
  461.  
  462. function TEnhPasParser.NextToken: Char;
  463. begin
  464.   SkipBlanks;
  465.   FTokenPtr := FSourcePtr;
  466.   case FSourcePtr^ of
  467.     '{':
  468.       begin { comment or compiler directive... }
  469.         FStringPtr := FSourcePtr;
  470.         Inc(FSourcePtr);  { check next char... }
  471.         while true do
  472.         begin
  473.           case FSourcePtr^ of
  474.             #0: begin
  475.               ReadBuffer;
  476.               FStringPtr := FSourcePtr;
  477.               if FSourcePtr^ = #0 then Break;
  478.               {$IFDEF DEBUG}
  479.               writeln(Log, 'in comment');
  480.               {$ENDIF}
  481.             end;
  482.             {$IFDEF FULLLINECOUNT}
  483.             #10: Inc(FSourceLine);
  484.             {$ENDIF}
  485.             '}':
  486.               begin
  487.                 Inc(FSourcePtr);
  488.                 Break;      { end comment... }
  489.               end;
  490.           end;
  491.           FStringPtr^ := FSourcePtr^;
  492.           Inc(FStringPtr);
  493.           Inc(FSourcePtr);
  494.         end;
  495.         FToken := toComment;
  496.         Result := FToken;
  497.       end;
  498.     '(', '/':  { possible comment or compiler directive... }
  499.       begin
  500.         FToken := FSourcePtr^; { assume NOT a comment }
  501.         Result := FToken;
  502.         FStringPtr := FSourcePtr;
  503.         Inc(FSourcePtr);  { check next char... }
  504.         case FSourcePtr^ of
  505.           '*':  { is a comment }
  506.             begin
  507.               Inc(FSourcePtr);  { check next char... }
  508.               while True do
  509.               begin
  510.                 case FSourcePtr^ of
  511.                   #0: begin
  512.                     ReadBuffer;
  513.                     FStringPtr := FSourcePtr;
  514.                     if FSourcePtr^ = #0 then Break;
  515.                     {$IFDEF DEBUG}
  516.                     writeln(Log, 'in comment');
  517.                     {$ENDIF}
  518.                   end;
  519.                   {$IFDEF FULLLINECOUNT}
  520.                   #10: Inc(FSourceLine);
  521.                   {$ENDIF}
  522.                   '*':
  523.                     begin
  524.                       Inc(FSourcePtr);
  525.                       if FSourcePtr^ = ')' then
  526.                       begin
  527.                         Inc(FSourcePtr);
  528.                         Break; { end of comment }
  529.                       end;
  530.                     end;
  531.                 end;
  532.                 FStringPtr^ := FSourcePtr^;
  533.                 Inc(FStringPtr);
  534.                 Inc(FSourcePtr);
  535.               end;
  536.               FToken := toComment;
  537.               Result := FToken;
  538.             end;
  539.           '/':  { is a comment }
  540.             begin
  541.               Inc(FSourcePtr);
  542.               while (FSourcePtr^ <> #13) do  { end of line, hence comment }
  543.               begin
  544.                 FStringPtr^ := FSourcePtr^;
  545.                 Inc(FStringPtr);
  546.                 Inc(FSourcePtr);
  547.               end;
  548.               FToken := toComment;
  549.               Result := FToken;
  550.             end;
  551.         end;
  552.       end;
  553.     else Result := inherited NextToken;
  554.   end;
  555. end;
  556.  
  557. { THtmlParser }
  558. function THtmlParser.TokenString: string;
  559. var
  560.   L: Integer;
  561. begin
  562.   if (FToken = toString) or (FToken = toOpenTag)
  563.     or (FToken = toCloseTag) then
  564.       L := FStringPtr - FTokenPtr else
  565.       L := FSourcePtr - FTokenPtr;
  566.   {$IFDEF Win32}
  567.   SetString(Result, FTokenPtr, L);
  568.   {$ELSE}
  569.   if L > 255 then L := 255;
  570.   Result[0] := Char(L);
  571.   {$ENDIF}
  572.   Move(FTokenPtr[0], Result[1], L);
  573. end;
  574.  
  575. function THtmlParser.NextToken: Char;
  576. begin
  577.   SkipBlanks;
  578.   FTokenPtr := FSourcePtr;
  579.   case FSourcePtr^ of
  580.     '<':   { is a tag }
  581.     begin
  582.       FStringPtr := FSourcePtr;
  583.       Inc(FSourcePtr);
  584.       case FSourcePtr^ of
  585.         '/':  { is an 'close' tag }
  586.           begin
  587.             Inc(FSourcePtr);
  588.             while true do
  589.             begin
  590.               case FSourcePtr^ of
  591.                 #0: begin
  592.                   ReadBuffer;
  593.                   FStringPtr := FSourcePtr;
  594.                   if FSourcePtr^ = #0 then Break;
  595.                 end;
  596.                 '>': begin
  597.                   Inc(FSourcePtr);
  598.                   Break; { end of tag }
  599.                 end;
  600.               end; {case}
  601.               FStringPtr^ := FSourcePtr^;
  602.               Inc(FStringPtr);
  603.               Inc(FSourcePtr);
  604.             end;
  605.             FToken := toCloseTag;
  606.             Result := FToken;
  607.           end;
  608.         else
  609.           begin
  610.             while true do
  611.             begin
  612.               case FSourcePtr^ of
  613.                 #0: begin
  614.                   ReadBuffer;
  615.                   FStringPtr := FSourcePtr;
  616.                   if FSourcePtr^ = #0 then Break;
  617.                 end;
  618.                 '>': begin
  619.                   Inc(FSourcePtr);
  620.                   Break; { end of tag }
  621.                 end;
  622.               end; {case}
  623.               FStringPtr^ := FSourcePtr^;
  624.               Inc(FStringPtr);
  625.               Inc(FSourcePtr);
  626.             end;
  627.             FToken := toOpenTag;
  628.             Result := FToken;
  629.           end;
  630.       end; {case}
  631.     end;
  632.     else Result := inherited NextToken;
  633.   end;
  634. end;
  635.  
  636. {$IFDEF DEBUG}
  637. initialization
  638.   AssignFile(Log, 'debug.log');
  639.   Rewrite(Log);
  640. finalization
  641.   CloseFile(Log);
  642. {$ENDIF}
  643. end.
  644.